package org.orbis.communicate.filter;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.TreeSet;
import org.orbis.communicate.FlagsResult;
import org.orbis.flag.SearchFlags;

public class RangeFilter implements Filter<FlagsResult[], SearchFlags>{
	@Override
	public FlagsResult[] intersection(SearchFlags flags, FlagsResult[]... part){
		ArrayList<FlagsResult> results = new ArrayList<FlagsResult>();

		int parts = part.length;
		FlagsResult[] currentID = new FlagsResult[parts];
		int[] currentPoint = new int[parts];
		for(int i = 0; i < parts; i++){
			currentID[i] = part[i][0];
			currentPoint[i] = 0;
		}

		boolean isSame, isBreak;
		long firstNum;
		int firstIndex;
		while(true){
			isSame = true;
			firstNum = Long.MAX_VALUE;
			firstIndex = 0;
			for(int i = 0; i < parts; i++){
//				System.out.println("Part " + i + ": " + currentID[i].ID());
				if(currentID[i].ID() < firstNum){
					firstIndex = i;
					firstNum = currentID[i].ID();
				}
				if(i == parts - 1) break;
				if(currentID[i].ID() != currentID[i + 1].ID()) isSame = false;
			}

			isBreak = false;
			if(isSame){
				// Same pointer is found
				results.add(currentID[0]);
				for(int i = 0; i < parts; i++){
					if(currentPoint[i] == part[i].length - 1){
						isBreak = true;
						break;
					}
					currentPoint[i]++;
					currentID[i] = part[i][currentPoint[i]];
				}
			}else{
				if(currentPoint[firstIndex] == part[firstIndex].length - 1){
					isBreak = true;
					break;
				}
				currentPoint[firstIndex]++;
				currentID[firstIndex] = part[firstIndex][currentPoint[firstIndex]];
			}
			if(isBreak) break;
		}

		return results.toArray(new FlagsResult[0]);
	}

	@Override
	public FlagsResult[] join(SearchFlags flags, FlagsResult[]... part){
		TreeSet<FlagsResult> results = new TreeSet<FlagsResult>();

		int parts = part.length;
		FlagsResult[] currentID = new FlagsResult[parts];
		BitSet reachEnd = new BitSet(parts);
		for(int i = 0; i < parts; i++)
			currentID[i] = part[i][0];

		// Stop if all parts get end point
		int count = 0;
		while(reachEnd.cardinality() != parts){
			for(int i = 0; i < parts; i++){
				if(!reachEnd.get(i)) results.add(part[i][count]);
				if(count == part[i].length - 1) reachEnd.set(i);
			}
			count++;
		}

		return results.toArray(new FlagsResult[0]);
	}

	private boolean filterBinary(SearchFlags flags, FlagsResult result){
		boolean[] binFlag = result.binaryFlags();
		for(int i = 0; i < binFlag.length; i++){
			if(!flags.inquiryBinary(i, binFlag[i])) return false;
		}

		return true;
	}

	private boolean filterInt(SearchFlags flags, FlagsResult result){
		if(result.intFlags() == null) return true;
		int[] intValues = result.intFlags();
		for(int i = 0; i < intValues.length; i++){
			if(!flags.inquiryInt(i, intValues[i])) return false;
		}

		return true;
	}

	private boolean filterLong(SearchFlags flags, FlagsResult result){
		if(result.longFlags() == null) return true;
		long[] longValues = result.longFlags();
		for(int i = 0; i < longValues.length; i++){
			if(!flags.inquiryLong(i, longValues[i])) return false;
		}

		return true;
	}

	private boolean filterFloat(SearchFlags flags, FlagsResult result){
		if(result.floatFlags() == null) return true;
		float[] floatValues = result.floatFlags();
		for(int i = 0; i < floatValues.length; i++){
			if(!flags.inquiryFloat(i, floatValues[i])) return false;
		}

		return true;
	}

	@Override
	public FlagsResult[] filter(SearchFlags filter, FlagsResult[] list){
		ArrayList<FlagsResult> result = new ArrayList<FlagsResult>(list.length);

		for(int i = 0; i < list.length; i++){
			if(!filterBinary(filter, list[i])) continue;
			if(!filterInt(filter, list[i])) continue;
			if(!filterLong(filter, list[i])) continue;
			if(!filterFloat(filter, list[i])) continue;
			result.add(list[i]);
		}

		return result.toArray(new FlagsResult[0]);
	}
}
